為了確保我們的應用程式結構清晰且易於維護,我們先從首頁進行元件拆分。首先,我們將主頁上的 Card
和 Header
元件拆分出來,使其可以在其他頁面或模組中重用。
A) ProductCard
libs\iron-components\src\lib\ProductCard\ProductCard.tsx
:
import { Box, Card, Text } from "@radix-ui/themes";
import React from "react";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ProductCardProps {}
const ProductCard: React.FC<ProductCardProps> = () => {
return (
<Card className="flex:1|1|20% flex:1|1|40%@<xs">
<Box>
<picture>
<img src="https://www.w3schools.com/tags/img_girl.jpg" aria-hidden alt="Sample Image" width="100%" />
</picture>
<Text as="div" size="2" weight="bold">
Teodros Girmay
</Text>
<Text as="div" size="2" color="gray">
Engineering
</Text>
</Box>
</Card>
)
}
export default ProductCard;
libs\iron-components\src\lib\ProductCard\ProductCard.stories.tsx
:
import { Meta, StoryObj } from "@storybook/react";
import ProductCard from "./ProductCard";
const meta: Meta<typeof ProductCard> = {
component: ProductCard,
};
export default meta;
type Story = StoryObj<typeof ProductCard>;
export const Default: Story = {
args: {},
};
libs\iron-components\src\lib\ProductCard\index.ts
:
import ProductCard from "./ProductCard";
export default ProductCard;
B) Header
libs\iron-components\src\lib\Header\Header.tsx
:
import React from "react";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface HeaderProps {}
const Header: React.FC<HeaderProps> = () => {
return (
<header className="flex flex-direction:row jc:space-between ai:center p:1em bg:#333 color:#fff">
<div className="flex-shrink:0 f:1.5rem f:bold">Iron Shop</div>
<nav className="flex-grow:1 flex m:0|1em">
<ul className="list-style:none flex gap:20px m:0 p:0 {cursor:pointer}>li">
<li>Home</li>
<li>Products</li>
</ul>
</nav>
</header>
)
}
export default Header;
libs\iron-components\src\lib\Header\Header.stories.tsx
:
import { Meta, StoryObj } from "@storybook/react";
import Header from "./Header";
const meta: Meta<typeof Header> = {
component: Header,
};
export default meta;
type Story = StoryObj<typeof Header>;
export const Default: Story = {
args: {},
};
libs\iron-components\src\lib\Header\index.ts
:
import Header from "./Header";
export default Header;
現在我們打開Storybook觀察我們的元件。
Header
ProductCard
接著,我們在libs\iron-components\src\index.ts
添加導出我們元件的路徑:
export * from "./lib/Header";
export * from "./lib/ProductCard";
export * from "./lib/iron-components";
現在我們在我們的Next app中引入。現在我們稍微修改一下我們的layout和client元件
打開apps\iron-ecommerce-next\app\layout.tsx
:
import { Flex } from "@radix-ui/themes";
import "@radix-ui/themes/styles.css";
import Header from "libs/iron-components/src/lib/Header";
import AppProvider from "./app-provider";
import "./global.css";
export const metadata = {
title: "Welcome to iron-ecommerce-next",
description: "Generated by create-nx-workspace"
};
const RootLayout = ({ children }: { children: React.ReactNode }) => {
return (
<html lang="en">
<body>
<AppProvider>
<Flex direction="column">
<Header />
{children}
</Flex>
</AppProvider>
</body>
</html>
);
};
export default RootLayout;
打開apps\iron-ecommerce-next\app\home.client.tsx
:
"use client";
import { Flex } from "@radix-ui/themes";
import ProductCard from "libs/iron-components/src/lib/ProductCard";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface Props {}
// eslint-disable-next-line no-empty-pattern
const HomeClient = ({}: Props) => {
return (
<Flex align="center" justify="center">
<section className="flex flex:wrap flex-direction:row flex-basis:xs flex-basis:full@<xs gap:1rem jc:center">
<div className="bg:blue w:100% h:10rem">Cover</div>
{Array.from({ length: 32 }).map((_, i) => (
<ProductCard key={i} />
))}
</section>
</Flex>
);
};
export default HomeClient;
現在我們已經將元件從頁面拆分出去了,接著,我們先將剩下的部分先完成,並且創建的部分有products、user和auth等頁面。並且下面是我們創建的結構。
a) products
///// apps\iron-ecommerce-next\app\products\page.tsx /////
import { NextPage } from "next";
import ProductsClient from "./products.client";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ProductsPageProps {}
const ProductsPage: NextPage<ProductsPageProps> = () => {
return <ProductsClient />;
};
export default ProductsPage;
///// apps\iron-ecommerce-next\app\products\products.client.tsx /////
"use client";
import { Flex } from "@radix-ui/themes";
import ProductCard from "libs/iron-components/src/lib/ProductCard";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ProductsProps {}
// eslint-disable-next-line no-empty-pattern
const ProductsClient = ({}: ProductsProps) => {
return (
<Flex align="center" justify="center">
<section className="flex flex:wrap flex-direction:row flex-basis:xs flex-basis:full@<xs gap:1rem jc:center">
<div className="w:100% h:10rem">Products</div>
{Array.from({ length: 32 }).map((_, i) => (
<ProductCard key={i} />
))}
</section>
</Flex>
);
};
export default ProductsClient;
///// apps\iron-ecommerce-next\app\products\[id]\page.tsx /////
import { NextPage } from "next";
import ProductIdClient from "./productId.client";
interface ProductIdPageProps {
params: {
id: string;
};
}
const ProductIdPage: NextPage<ProductIdPageProps> = ({ params }) => {
const { id } = params;
return <ProductIdClient productId={id} />;
};
export default ProductIdPage;
///// apps\iron-ecommerce-next\app\products\[id]\productId.client.tsx /////
"use client";
import { Flex } from "@radix-ui/themes";
import ProductCard from "libs/iron-components/src/lib/ProductCard";
interface ProductIdProps {
productId?: string;
}
// eslint-disable-next-line no-empty-pattern
const ProductIdClient = ({productId}: ProductIdProps) => {
return (
<Flex align="center" justify="center">
<section className="flex flex:wrap flex-direction:row flex-basis:xs flex-basis:full@<xs gap:1rem jc:center">
<div className="w:100% h:10rem">Product: {productId}</div>
<ProductCard />
</section>
</Flex>
);
};
export default ProductIdClient;
b) user
///// apps\iron-ecommerce-next\app\user\[id]\page.tsx /////
import { NextPage } from "next";
import UserIdClient from "./userId.client";
interface UserIdPageProps {
params: {
id: string;
};
}
const UserIdPage: NextPage<UserIdPageProps> = ({ params }) => {
const { id } = params;
return <UserIdClient userId={id} />;
};
export default UserIdPage;
///// apps\iron-ecommerce-next\app\user\[id]\userId.client.tsx /////
"use client";
import { Flex } from "@radix-ui/themes";
interface UserIdProps {
userId?: string;
}
// eslint-disable-next-line no-empty-pattern
const UserIdClient = ({userId}: UserIdProps) => {
return (
<Flex align="center" justify="center">
<section className="flex flex:wrap flex-direction:row flex-basis:xs flex-basis:full@<xs gap:1rem jc:center">
<div className="w:100% h:10rem bg:grey">User {userId} Block</div>
</section>
</Flex>
);
};
export default UserIdClient;
///// apps\iron-ecommerce-next\app\user\auth\page.tsx /////
import { NextPage } from "next";
import UserAuthClient from "./userAuth.client";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface UserIdPageProps {}
const UserAuthPage: NextPage<UserIdPageProps> = () => {
return <UserAuthClient />;
};
export default UserAuthPage;
///// apps\iron-ecommerce-next\app\user\auth\userAuth.client.tsx /////
"use client";
import { Flex } from "@radix-ui/themes";
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface UserAuthProps {}
// eslint-disable-next-line no-empty-pattern
const UserAuthClient = ({}: UserAuthProps) => {
return (
<Flex align="center" justify="center">
<section className="flex flex:wrap flex-direction:row flex-basis:xs flex-basis:full@<xs gap:1rem jc:center">
<div className="w:100% h:10rem bg:grey">User Auth Block</div>
</section>
</Flex>
);
};
export default UserAuthClient;
現在我們將部分的頁面先創建出來,之後我們在實現內部的細節。
並且當我們進行更多的開發,我們可能會需要一直創建組件。手動創建這些組件可能會很耗時,並且容易出錯。為了提高效率,我們將使用 NX 提供的工具來自動化這個過程。
今天,我們在Next.js應用程式中拆分和重用組件,並通過使用 Storybook,我們可以更加直觀地理解和展示我們的組件。接著我們將一些頁面先創建起來。最後,為了進一步提高我們的開發效率,明天我們將介紹如何使用NX來達成自動化創建組件。
樓主你好~
我在此章節執行
git add .
pnpm run commit
後
一直報以下錯誤
✖ nx affected:lint:
> NX Affected criteria defaulted to --base=main --head=HEAD
> NX Running target lint for 3 projects:
- iron-ecommerce-next
- iron-ecommerce-next-e2e
- iron-components
With additional flags:
apps/iron-ecommerce-next/app/home.client.tsx apps/iron-ecommerce-next/app/layout.tsx apps/iron-ecommerce-next/app/products/[id]/page.tsx apps/iron-ecommerce-next/app/products/[id]/productId.client.tsx apps/iron-ecommerce-next/app/products/page.tsx apps/iron-ecommerce-next/app/products/products.client.tsx apps/iron-ecommerce-next/app/user/[id]/page.tsx apps/iron-ecommerce-next/app/user/[id]/userId.client.tsx apps/iron-ecommerce-next/app/user/auth/page.tsx apps/iron-ecommerce-next/app/user/auth/userAuth.client.tsx libs/iron-components/src/index.ts libs/iron-components/src/lib/Header/Header.stories.tsx libs/iron-components/src/lib/Header/Header.tsx libs/iron-components/src/lib/Header/index.ts libs/iron-components/src/lib/ProductCard/ProductCard.stories.tsx libs/iron-components/src/lib/ProductCard/ProductCard.tsx libs/iron-components/src/lib/ProductCard/index.ts
> nx run iron-components:lint apps/iron-ecommerce-next/app/home.client.tsx apps/iron-ecommerce-next/app/layout.tsx apps/iron-ecommerce-next/app/products/[id]/page.tsx apps/iron-ecommerce-next/app/products/[id]/productId.client.tsx apps/iron-ecommerce-next/app/products/page.tsx apps/iron-ecommerce-next/app/products/products.client.tsx apps/iron-ecommerce-next/app/user/[id]/page.tsx apps/iron-ecommerce-next/app/user/[id]/userId.client.tsx apps/iron-ecommerce-next/app/user/auth/page.tsx apps/iron-ecommerce-next/app/user/auth/userAuth.client.tsx libs/iron-components/src/index.ts libs/iron-components/src/lib/Header/Header.stories.tsx libs/iron-components/src/lib/Header/Header.tsx libs/iron-components/src/lib/Header/index.ts libs/iron-components/src/lib/ProductCard/ProductCard.stories.tsx libs/iron-components/src/lib/ProductCard/ProductCard.tsx libs/iron-components/src/lib/ProductCard/index.ts
Linting "iron-components"...
Pages directory cannot be found at apps/iron-ecommerce-next/pages. If using a custom path, please configure with the `no-html-link-for-pages` rule in your eslint config file.
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/home.client.tsx
3:1 error Projects cannot be imported by a relative or absolute path, and must begin with a npm scope @nx/enforce-module-boundaries
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/layout.tsx
3:1 error Projects cannot be imported by a relative or absolute path, and must begin with a npm scope @nx/enforce-module-boundaries
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/products/products.client.tsx
3:1 error External resources cannot be imported using a relative or absolute path @nx/enforce-module-boundaries
✖ 3 problems (3 errors, 0 warnings)
✖ 3 problems (3 errors, 0 warnings)
> nx run iron-ecommerce-next-e2e:lint apps/iron-ecommerce-next/app/home.client.tsx apps/iron-ecommerce-next/app/layout.tsx apps/iron-ecommerce-next/app/products/[id]/page.tsx apps/iron-ecommerce-next/app/products/[id]/productId.client.tsx apps/iron-ecommerce-next/app/products/page.tsx apps/iron-ecommerce-next/app/products/products.client.tsx apps/iron-ecommerce-next/app/user/[id]/page.tsx apps/iron-ecommerce-next/app/user/[id]/userId.client.tsx apps/iron-ecommerce-next/app/user/auth/page.tsx apps/iron-ecommerce-next/app/user/auth/userAuth.client.tsx libs/iron-components/src/index.ts libs/iron-components/src/lib/Header/Header.stories.tsx libs/iron-components/src/lib/Header/Header.tsx libs/iron-components/src/lib/Header/index.ts libs/iron-components/src/lib/ProductCard/ProductCard.stories.tsx libs/iron-components/src/lib/ProductCard/ProductCard.tsx libs/iron-components/src/lib/ProductCard/index.ts
Linting "iron-ecommerce-next-e2e"...
Pages directory cannot be found at apps/iron-ecommerce-next/pages. If using a custom path, please configure with the `no-html-link-for-pages` rule in your eslint config file.
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/home.client.tsx
3:1 error Projects cannot be imported by a relative or absolute path, and must begin with a npm scope @nx/enforce-module-boundaries
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/layout.tsx
3:1 error Projects cannot be imported by a relative or absolute path, and must begin with a npm scope @nx/enforce-module-boundaries
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/products/products.client.tsx
3:1 error External resources cannot be imported using a relative or absolute path @nx/enforce-module-boundaries
✖ 3 problems (3 errors, 0 warnings)
✖ 3 problems (3 errors, 0 warnings)
> nx run iron-ecommerce-next:lint apps/iron-ecommerce-next/app/home.client.tsx apps/iron-ecommerce-next/app/layout.tsx apps/iron-ecommerce-next/app/products/[id]/page.tsx apps/iron-ecommerce-next/app/products/[id]/productId.client.tsx apps/iron-ecommerce-next/app/products/page.tsx apps/iron-ecommerce-next/app/products/products.client.tsx apps/iron-ecommerce-next/app/user/[id]/page.tsx apps/iron-ecommerce-next/app/user/[id]/userId.client.tsx apps/iron-ecommerce-next/app/user/auth/page.tsx apps/iron-ecommerce-next/app/user/auth/userAuth.client.tsx libs/iron-components/src/index.ts libs/iron-components/src/lib/Header/Header.stories.tsx libs/iron-components/src/lib/Header/Header.tsx libs/iron-components/src/lib/Header/index.ts libs/iron-components/src/lib/ProductCard/ProductCard.stories.tsx libs/iron-components/src/lib/ProductCard/ProductCard.tsx libs/iron-components/src/lib/ProductCard/index.ts
Linting "iron-ecommerce-next"...
Pages directory cannot be found at apps/iron-ecommerce-next/pages. If using a custom path, please configure with the `no-html-link-for-pages` rule in your eslint config file.
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/home.client.tsx
3:1 error Projects cannot be imported by a relative or absolute path, and must begin with a npm scope @nx/enforce-module-boundaries
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/layout.tsx
3:1 error Projects cannot be imported by a relative or absolute path, and must begin with a npm scope @nx/enforce-module-boundaries
/Users/chihhao/iron-ecommerce-org/apps/iron-ecommerce-next/app/products/products.client.tsx
3:1 error External resources cannot be imported using a relative or absolute path @nx/enforce-module-boundaries
✖ 3 problems (3 errors, 0 warnings)
✖ 3 problems (3 errors, 0 warnings)